package com.nulldev.util.concurrency.threadIt.v4.deadlocks;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class DeadlockDetector {
	private final long period;
	private final TimeUnit unit;
	private final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
	private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {

		@Override
		public Thread newThread(Runnable r) {
			return new Thread(r, "threadIt! v4: Deadlock Detection Thread");
		}
	});
	private final List<IDeadlockEvent> events = new ArrayList<IDeadlockEvent>();

	private final Runnable deadlockCheckPayload = new Runnable() {

		@Override
		public void run() {
			final long[] deadlockedThreadIds = DeadlockDetector.this.mbean.findDeadlockedThreads();
			if (deadlockedThreadIds != null && deadlockedThreadIds.length > 0) {
				final ThreadInfo[] threadInfos = DeadlockDetector.this.mbean.getThreadInfo(deadlockedThreadIds);
				if (threadInfos != null && threadInfos.length > 0) {
					final DeadlockMeta meta = new DeadlockMeta(threadInfos);
					for (final IDeadlockEvent _e : DeadlockDetector.this.events) {
						try {
							_e.deadlock(meta);
						} catch (Exception e) {
							if (System.err != null) {
								e.printStackTrace(System.err);
							}
						}
					}
				}
			}
		}
	};

	public DeadlockDetector(final long period, final TimeUnit unit) {
		this.period = period;
		this.unit = unit;
	}

	public boolean submitHandler(final IDeadlockEvent event) {
		return this.events.add(event);
	}

	public void start() {
		this.scheduler.scheduleAtFixedRate(this.deadlockCheckPayload, this.period, this.period, this.unit);
	}
}
